﻿using System;
using System.Linq;
using System.ComponentModel;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Drawing;

namespace GeoPal
{
    /// <summary>
    /// Delegate that updates the status message
    /// </summary>
    /// <param name="message">New status message</param>
    public delegate void UpdateStatusMessageDelegate(string message);
    /// <summary>
    /// Delegate that updates the connection status
    /// </summary>
    /// <param name="status">New connection status</param>
    public delegate void UpdateConnectionStatusDelegate(Status.ConnectionStatus status);
    /// <summary>
    /// Delegate that refreshes the map panel
    /// </summary>
    public delegate void MapRefreshDelegate();
    /// <summary>
    /// Delegate that refreshes the status panel
    /// </summary>
    public delegate void StatusRefreshDelegate();
    /// <summary>
    /// Application logic of formMain form
    /// </summary>
    public partial class formMain : Form
    {
        /// <summary>
        /// Double-bufferable control
        /// </summary>
        public class Canvas : Control
        {
            /// <summary>
            /// Does nothing when control becomes invalid
            /// </summary>
            /// <param name="e"></param>
            protected override void OnPaintBackground(PaintEventArgs e)
            {
                // Do nothing...
            }
        }
        /// <summary>
        /// Current formSettings form
        /// </summary>
        formSettings fSettings;
        /// <summary>
        /// Current formStatistics form
        /// </summary>
        formStatistics fStatistics;
        /// <summary>
        /// Current Gps module
        /// </summary>
        Gps gpsHandler;
        /// <summary>
        /// Current Status module
        /// </summary>
        Status status = new Status();
        /// <summary>
        /// Current Contacts module
        /// </summary>
        Contacts contacts = new Contacts();
        /// <summary>
        /// Current Communicator module
        /// </summary>
        Communicator communicator = new Communicator();
        /// <summary>
        /// Current Mapper module
        /// </summary>
        Mapper map = new Mapper(480, 480);
        /// <summary>
        /// Current Settings module
        /// </summary>
        Settings settings = new Settings();
        /// <summary>
        /// Current Twitter module
        /// </summary>
        Yedda.Twitter twitter = new Yedda.Twitter();

        MapRefreshDelegate mapRefreshDelegate;
        StatusRefreshDelegate statusRefreshDelegate;

        private Bitmap bmpMapOffscreen;
        private Bitmap bmpStatusOffscreen;
        /// <summary>
        /// Double-buffered panel that displays the map/radar
        /// </summary>
        private Canvas pMap;
        /// <summary>
        /// Double-buffered panel that displays the status indicators
        /// </summary>
        private Canvas pStatus;
        /// <summary>
        /// Constructor of formMain class (initializes default values)
        /// </summary>
        public formMain()
        {
            this.pMap = new Canvas();
            this.pMap.BackColor = System.Drawing.Color.Transparent;
            this.pMap.Dock = System.Windows.Forms.DockStyle.Top;
            this.pMap.Location = new System.Drawing.Point(0, 0);
            this.pMap.Name = "pMap";
            this.pMap.Size = new System.Drawing.Size(480, 480);
            this.pMap.Paint += new System.Windows.Forms.PaintEventHandler(this.pMap_Paint);
            this.pMap.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pMap_MouseDown);
            this.Controls.Add(this.pMap);

            this.pStatus = new Canvas();
            this.pStatus.BackColor = System.Drawing.Color.Transparent;
            this.pStatus.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.pStatus.Location = new System.Drawing.Point(0, 481);
            this.pStatus.Name = "pStatus";
            this.pStatus.Size = new System.Drawing.Size(480, 55);
            this.pStatus.Paint += new System.Windows.Forms.PaintEventHandler(this.pStatus_Paint);
            this.pStatus.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pStatus_MouseDown);
            this.Controls.Add(this.pStatus);

            InitializeComponent();

            mapRefreshDelegate = new MapRefreshDelegate(pMap.Refresh);
            statusRefreshDelegate = new StatusRefreshDelegate(pStatus.Refresh);


            fSettings = new formSettings(ref settings);
            fStatistics = new formStatistics(ref gpsHandler, ref communicator);

            communicator.updateContactsCallback = new UpdateContactsDelegate(contacts.UpdateContacts);
            communicator.updateMessagesCallback = new UpdateMessagesDelegate(contacts.UpdateMessages);
            communicator.updateStatusMessageCallback = new UpdateStatusMessageDelegate(UpdateStatusMessage);
            communicator.updateConnectionStatusCallback = new UpdateConnectionStatusDelegate(UpdateConnectionStatus);
            communicator.mapRefreshCallback = new MapRefreshDelegate(UpdateMap);
            map.mapRefreshCallback = new MapRefreshDelegate(UpdateMap);
            map.updateMessageCallback = new UpdateStatusMessageDelegate(UpdateStatusMessage);

            bmpMapOffscreen = new Bitmap(480, 480);
            bmpStatusOffscreen = new Bitmap(480, 55);
            timerRequest.Interval = (int)settings.RequestInterval * 1000;
            timerUpdate.Interval = (int)settings.UpdateInterval * 1000;
            map.LoadSplashScreen();
            if (String.IsNullOrEmpty(settings.UserName))
            {
                System.Windows.Forms.MessageBox.Show("Please register!");
            }
        }
        /// <summary>
        /// Event handler for the location update timer
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void timerUpdate_Tick(object sender, EventArgs e)
        {
            if (menuItemTrackMe.Checked)
            {
                if (settings.IsEnergySave)
                {
                    gpsHandler.SwitchOn();
                }
                if (gpsHandler.IsFixed)
                {
                    UpdateConnectionStatus(Status.ConnectionStatus.Progress);
                    communicator.UpdatePosition(settings, gpsHandler);
                }
            }
        }
        /// <summary>
        /// Event handler of the contact location request timer
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void timerRequest_Tick(object sender, EventArgs e)
        {
            communicator.GetContactList(settings);
            communicator.GetMessages(settings);
        }
        /// <summary>
        /// Initializes the GPS module (so the GPS reveiver)
        /// </summary>
        /// <returns>True, if the initializaton was successful. False, if it wasn't.</returns>
        private bool InitGps()
        {
            UpdateStatusMessage("Initializing GPS...");
            if (gpsHandler == null)
            {
                gpsHandler = new Gps(ref settings);
                gpsHandler.PositionFixedEvent += new Gps.PositionFixedDelegate(gpsHandler_PositionFixedEvent);
            }
            if (gpsHandler != null)
            {
                timerUpdate.Enabled = true;
                menuItemGps.Checked = true;
                if (gpsHandler.IsFixed)
                {
                    UpdateGpsStatus(Status.GpsStatus.Fixed);
                }
                else
                {
                    UpdateGpsStatus(Status.GpsStatus.Enabled);
                }
                UpdateStatusMessage("GPS Initialized.");
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// Initalizes the Contacts module (contact list)
        /// </summary>
        private void InitContactList()
        {
            UpdateConnectionStatus(Status.ConnectionStatus.Progress);
            communicator.GetContactList(settings);
            timerRequest.Enabled = true;
        }
        /// <summary>
        /// Deactivates the GPS receiver
        /// </summary>
        private void DestroyGps()
        {
            gpsHandler.Dispose();
            gpsHandler = null;
            menuItemGps.Checked = false;
            timerUpdate.Enabled = false;
            UpdateGpsStatus(Status.GpsStatus.Disabled);
        }
        /// <summary>
        /// Handles the tap on the map panel
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments (X,Y coordinates)</param>
        private void pMap_MouseDown(object sender, MouseEventArgs e)
        {
            switch (map.state)
            {
                case Mapper.State.Splash:
                    InitGps();
                    InitContactList();
                    map.SwitchToRadar();
                    pMap.Refresh();
                    break;
                case Mapper.State.Radar:
                    map.DownloadGoogleMap(ref settings);
                    map.state = Mapper.State.GoogleMap;
                    break;
                case Mapper.State.GoogleMap:
                    map.SetCenter(e.X, e.Y);
                    if (map.zoomLevel < 14)
                    {
                        map.zoomLevel = map.zoomLevel + 4;
                    }
                    map.DownloadGoogleMap(ref settings);
                    break;
                default:
                    System.Windows.Forms.MessageBox.Show("Impossible!");
                    break;

            }
        }
        /// <summary>
        /// Repaints the map panel according to the current state of the map
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void pMap_Paint(object sender, PaintEventArgs e)
        {
            Graphics gxOff = Graphics.FromImage(bmpMapOffscreen);
            gxOff.Clear(this.BackColor);
            gxOff.DrawImage(map.curMap, 0, 0);
            switch (map.state)
            {
                case Mapper.State.Splash:
                    break;
                case Mapper.State.Radar:
                    if (gpsHandler != null)
                    {
                        map.UpdateRadar(ref gxOff, contacts, gpsHandler.position);
                    }
                    break;
                case Mapper.State.GoogleMap:
                    map.UpdateMarkers(ref gxOff, contacts);
                    break;
                default:
                    break;
            }
            if (menuItemShowMessages.Checked)
            {
                gxOff.DrawString(contacts.GetMessageString(), new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular), new SolidBrush(Color.Black), new Rectangle(5, 5, 470, 470));
            }
//            gxOff.DrawString(map.text, new Font(FontFamily.GenericSansSerif, 8, FontStyle.Regular), new SolidBrush(Color.Black), new Rectangle(5, 5, 470, 470));
            gxOff.Dispose();
            e.Graphics.DrawImage(bmpMapOffscreen, 0, 0);
        }
        /// <summary>
        /// Displays the Statistics form
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemShowStatistics_Click(object sender, EventArgs e)
        {
            fStatistics.updateObjects(ref gpsHandler, ref communicator);
            fStatistics.ShowDialog();
        }
        /// <summary>
        /// Displays the Settings form
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemSets_Click(object sender, EventArgs e)
        {
            fSettings.ShowDialog();
        }
        /// <summary>
        /// Activates/deactivates the Gps module
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemGps_Click(object sender, EventArgs e)
        {
            if (gpsHandler != null)
            {
                if (menuItemGps.Checked)
                {
                    if (gpsHandler.SwitchOff())
                    {
                        menuItemGps.Checked = false;
                    }
                }
                else
                {
                    if (gpsHandler.SwitchOn())
                    {
                        menuItemGps.Checked = true;
                    }
                }
            }
        }
        /// <summary>
        /// Closes the application
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemExit_Click(object sender, EventArgs e)
        {
            this.Close();
        }
        /// <summary>
        /// Updates the map (indirectly)
        /// </summary>
        public void UpdateMap()
        {
            pMap.Invoke(mapRefreshDelegate);
        }
        /// <summary>
        /// Updates the status message (indirectly)
        /// </summary>
        /// <param name="text">New status message</param>
        public void UpdateStatusMessage(string text)
        {
            status.statusMessage = DateTime.Now.ToLongTimeString() + ": " + text;
            pStatus.Invoke(statusRefreshDelegate);
            fStatistics.AddLogString(text);
        }
        /// <summary>
        /// Updates the connection status icon (indirectly)
        /// </summary>
        /// <param name="connectionStatus">Connection status</param>
        public void UpdateConnectionStatus(Status.ConnectionStatus connectionStatus)
        {
            status.connectionStatus = connectionStatus;
            pStatus.Invoke(statusRefreshDelegate);
        }
        /// <summary>
        /// Updates the GPS status icon (indirectly)
        /// </summary>
        /// <param name="gpsStatus">GPS Status</param>
        public void UpdateGpsStatus(Status.GpsStatus gpsStatus)
        {
            status.gpsStatus = gpsStatus;
            pStatus.Invoke(statusRefreshDelegate);
        }
        /// <summary>
        /// Repaints the status panel according to the current status indicators
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void pStatus_Paint(object sender, PaintEventArgs e)
        {
            Graphics gxOff = Graphics.FromImage(bmpStatusOffscreen);
            gxOff.Clear(this.BackColor);

            gxOff.DrawImage(status.imageStatusBackground, 0, 0);
            gxOff.DrawString(status.statusMessage, status.statusFont, status.statusBrush, status.statusTextRectangle);
            switch (status.connectionStatus)
            {
                case Status.ConnectionStatus.Connected:
                    gxOff.DrawIcon(status.iconNetworkOk, status.statusIconNetworkRectangle.X, status.statusIconNetworkRectangle.Y);
                    break;
                case Status.ConnectionStatus.Progress:
                    gxOff.DrawIcon(status.iconNetworkProgress, status.statusIconNetworkRectangle.X, status.statusIconNetworkRectangle.Y);
                    break;
                default:
                    gxOff.DrawIcon(status.iconNetworkBad, status.statusIconNetworkRectangle.X, status.statusIconNetworkRectangle.Y);
                    break;
            }

            switch (status.gpsStatus)
            {
                case Status.GpsStatus.Enabled:
                    gxOff.DrawIcon(status.iconGpsOk, status.statusIconGpsRectangle.X, status.statusIconGpsRectangle.Y);
                    break;
                case Status.GpsStatus.Fixed:
                    gxOff.DrawIcon(status.iconGpsOk, status.statusIconGpsRectangle.X, status.statusIconGpsRectangle.Y);
                    break;
                default:
                    gxOff.DrawIcon(status.iconGpsBad, status.statusIconGpsRectangle.X, status.statusIconGpsRectangle.Y);
                    break;
            }

            gxOff.Dispose();
            e.Graphics.DrawImage(bmpStatusOffscreen, 0, 0);
        }
        /// <summary>
        /// Fits the map to the contacts
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemShowGoogleFit_Click(object sender, EventArgs e)
        {
            map.state = Mapper.State.GoogleMap;
            map.FitToContacts(contacts);
            map.DownloadGoogleMap(ref settings);
        }
        /// <summary>
        /// Switches the map state to radar and repaints the map
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemShowRadar_Click(object sender, EventArgs e)
        {
            map.SwitchToRadar();
            pMap.Refresh();
        }
        /// <summary>
        /// Handles the tap on the status panel
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments (X,Y coordinates)</param>
        private void pStatus_MouseDown(object sender, MouseEventArgs e)
        {
            if(status.statusIconGpsRectangle.Contains(e.X, e.Y))
            {
                if (status.gpsStatus == Status.GpsStatus.Disabled)
                {
                    InitGps();
                }
                else
                {
                    DestroyGps();
                }
            }
            if(status.statusIconNetworkRectangle.Contains(e.X, e.Y))
            {
                if (status.connectionStatus == Status.ConnectionStatus.Disconnected)
                {
                    InitContactList();
                }
                else
                {
                    status.connectionStatus = Status.ConnectionStatus.Disconnected;
                    timerRequest.Enabled = false;
                }
            }
            pStatus.Refresh();
        }
        /// <summary>
        /// Switches map state to world map
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemShowGoogleWorld_Click(object sender, EventArgs e)
        {
            map.state = Mapper.State.GoogleMap;
            map.zoomLevel = 1;
            map.DownloadGoogleMap(ref settings);
        }
        /// <summary>
        /// Show/hide messages on the map
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemShowMessages_Click(object sender, EventArgs e)
        {
            if (this.menuItemShowMessages.Checked)
            {
                map.showMessages = false;
                this.menuItemShowMessages.Checked = false;
            }
            else
            {
                map.showMessages = true;
                this.menuItemShowMessages.Checked = true;
            }
            pMap.Refresh();
        }
        /// <summary>
        /// Displays the panel where user can send messages
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemSendMsg_Click(object sender, EventArgs e)
        {
            if (!pMessage.Visible)
            {
                tbMessage.Text = "";
                cbContactList.Items.Clear();
                foreach (Contacts.Buddy contact in contacts.contactList)
                {
                    cbContactList.Items.Add(contact.name);
                }
                pMessage.BringToFront();
            }
            pMessage.Visible = !pMessage.Visible;
        }
        /// <summary>
        /// Sends the message
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void btSendMessage_Click(object sender, EventArgs e)
        {
            if (cbContactList.SelectedItem != null)
            {
                communicator.SendMessage(settings, cbContactList.SelectedItem.ToString(), tbMessage.Text);
            }

            pMessage.Visible = false;
        }
        /// <summary>
        /// Handles key strokes on the map/radar
        /// On map: pan and zoom out
        /// On radar: zoom in/out
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void formMain_KeyDown(object sender, KeyEventArgs e)
        {
            if (map.state == Mapper.State.GoogleMap)
            {
                if ((e.KeyCode == System.Windows.Forms.Keys.Up))
                {
                    map.SetCenter(240, 0);
                }
                if ((e.KeyCode == System.Windows.Forms.Keys.Down))
                {
                    map.SetCenter(240, 480);
                }
                if ((e.KeyCode == System.Windows.Forms.Keys.Left))
                {
                    map.SetCenter(0, 240);
                }
                if ((e.KeyCode == System.Windows.Forms.Keys.Right))
                {
                    map.SetCenter(480, 240);
                }
                if ((e.KeyCode == System.Windows.Forms.Keys.Enter))
                {
                    if (!map.ZoomOut())
                    {
                        System.Windows.Forms.MessageBox.Show("You have reached the minimum zoom level!");
                    };
                }
                map.DownloadGoogleMap(ref settings);
            }
            else if (map.state == Mapper.State.Radar)
            {
                if ((e.KeyCode == System.Windows.Forms.Keys.Up))
                {
                    map.ZoomIn();
                }
                if ((e.KeyCode == System.Windows.Forms.Keys.Down))
                {
                    map.ZoomOut();
                }
                pMap.Refresh();
            }
            else if (map.state == Mapper.State.Splash)
            {
                InitGps();
                InitContactList();
                map.SwitchToRadar();
                pMap.Refresh();
            }
        }
        /// <summary>
        /// Handles the event when the GPS position fix changes
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="isFixed">Is the position fixed?</param>
        private void gpsHandler_PositionFixedEvent(object sender, bool isFixed)
        {
            if (isFixed)
            {
                UpdateStatusMessage("GPS position fixed.");
                UpdateGpsStatus(Status.GpsStatus.Fixed);
                UpdateMap();
                if (settings.IsEnergySave)
                {
                    gpsHandler.SwitchOff();
                }
            }
            else
            {
                UpdateStatusMessage("GPS position lost.");
                UpdateGpsStatus(Status.GpsStatus.Enabled);
            }
        }
        /// <summary>
        /// Shows credits
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemAbout_Click(object sender, EventArgs e)
        {
            System.Windows.Forms.MessageBox.Show("Geopal beta\r\n0.2 build 20081102\r\nBME-TMIT");
        }
        /// <summary>
        /// Send Twitter message
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void btTwit_Click(object sender, EventArgs e)
        {
            twitter.Update(settings.TwitterName, settings.TwitterPass, tbMessage.Text, Yedda.Twitter.OutputFormatType.XML);
        }
        /// <summary>
        /// Changes the state of user tracking
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemTrackMe_Click(object sender, EventArgs e)
        {
            menuItemTrackMe.Checked = !menuItemTrackMe.Checked;
        }
        /// <summary>
        /// Zooms in map
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemZoomIn_Click(object sender, EventArgs e)
        {
            if (map.state == Mapper.State.GoogleMap && map.zoomLevel < 15)
            {
                map.zoomLevel--;
                map.DownloadGoogleMap(ref settings);
            }
        }
        /// <summary>
        /// Zooms out map
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemZoomOut_Click(object sender, EventArgs e)
        {
            if (map.state == Mapper.State.GoogleMap && map.zoomLevel > 1)
            {
                map.zoomLevel--;
                map.DownloadGoogleMap(ref settings);
            }
        }
        /// <summary>
        /// Shows friend manage panel
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemManageFriends_Click(object sender, EventArgs e)
        {
            pManageFriends.Visible = !tabControlFriends.Visible;
            pManageFriends.BringToFront();
        }
        /// <summary>
        /// Handles the changing of the currenty managed friend
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void lbFollowedUsers_SelectedIndexChanged(object sender, EventArgs e)
        {
            // TODO: frissíteni a labeleket az adott friend alapján
        }
        /// <summary>
        /// Refreshes friend list
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void tabControlFriends_GotFocus(object sender, EventArgs e)
        {
            lbFollowedUsers.Items.Clear();
            foreach (Contacts.Buddy buddy in contacts.contactList)
            {
                lbFollowedUsers.Items.Add(buddy.name);
            }
            
        }
        /// <summary>
        /// Requests contact list immediately
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemUpdateContactsNow_Click(object sender, EventArgs e)
        {
            timerRequest_Tick(this, null);
        }
        /// <summary>
        /// Updates user position immediately
        /// </summary>
        /// <param name="sender">Sender object</param>
        /// <param name="e">Event arguments</param>
        private void menuItemUpdatePositionNow_Click(object sender, EventArgs e)
        {
            if (gpsHandler != null)
            {
                if (settings.IsEnergySave)
                {
                    gpsHandler.SwitchOn();
                }
                if (gpsHandler.IsFixed)
                {
                    timerUpdate_Tick(this, null);
                }
                else
                {
                    System.Windows.Forms.MessageBox.Show("No fixed position!");
                }
            }
            else
            {
                System.Windows.Forms.MessageBox.Show("GPS receiver inactive!");
            }
        }

    }
}